#!/bin/bash

zpackage::get_package_path() {
  local name=$1
  if [ -z "$name" ]; then
    log::error "[$(timestamp)][zpackage::get_package_path] name is required"
    exit 1
  fi

  echo $PACKAGES_PATH/$name
}

zpackage::get_package_repo_url() {
  local name=$1
  if [ -z "$name" ]; then
    log::error "[$(timestamp)][zpackage::get_package_repo_url] name is required"
    exit 1
  fi

  echo ${PACKAGES_REPO_PREFIX}${name}
}

zpackage::get_mod_path() {
  local name=$1
  if [ -z "$name" ]; then
    log::error "[$(timestamp)][zpackage::get_mod_path] name is required"
    exit 1
  fi

  local package_path=$(zpackage::get_package_path $name)
  echo $package_path/mod
}

zpackage::get_config_path() {
  local name=$1
  if [ -z "$name" ]; then
    log::error "[$(timestamp)][zpackage::get_config_path] name is required"
    exit 1
  fi

  local package_path=$(zpackage::get_package_path $name)
  echo $package_path/config
}

zpackage::get_core_path() {
  local name=$1
  if [ -z "$name" ]; then
    log::error "[$(timestamp)][zpackage::get_core_path] name is required"
    exit 1
  fi

  local package_path=$(zpackage::get_package_path $name)
  echo $package_path/core
}

zpackage::get_bin_zmicro_path() {
  local name=$1
  if [ -z "$name" ]; then
    log::error "[$(timestamp)][zpackage::get_bin_zmicro_path] name is required"
    exit 1
  fi

  local package_path=$(zpackage::get_package_path $name)
  echo $package_path/bin/zmicro
}

zpackage::load_mod() {
  local name=$1
  if [ -z "$name" ]; then
    log::error "[$(timestamp)][zpackage::load_mod] name is required"
    exit 1
  fi

  local package_mod_path=$(zpackage::get_mod_path $name)
  if [ ! -f $package_mod_path ]; then
    log::error "[$(timestamp)][zpackage::load_mod] name is required"
    exit 1
  fi

  config::load_file $package_mod_path
}

zpackage::load_config() {
  local name=$1
  if [ -z "$name" ]; then
    log::error "[$(timestamp)][zpackage::load_config] name is required"
    exit 1
  fi

  local config_path=$(zpackage::get_config_path $name)
  if [ -f $config_path ]; then
    config::load_file $config_path
  fi
}

zpackage::load_core() {
  local name=$1
  if [ -z "$name" ]; then
    log::error "[$(timestamp)][zpackage::load_core] name is required"
    exit 1
  fi

  local core_path=$(zpackage::get_core_path $name)
  if [ -f $core_path ]; then
    config::load_file $core_path
  fi
}

zpackage::register() {
  local name=$1
  if [ -z "$name" ]; then
    log::error "[$(timestamp)][zpackage::register] name is required"
    exit 1
  fi

  local package_path=$(zpackage::get_package_path $name)
  local package_bin_zmicro_path=$(zpackage::get_bin_zmicro_path $name)
  if [ -d $package_bin_zmicro_path ]; then
    run() {
      local index=$1
      local bin=$2
      local source_path=$package_path/bin/zmicro/$bin
      local target_path=$ZMICRO_HOME/bin/sub/$bin

      if [ ! -f $source_path ]; then
        log::error "[$(timestamp)][zpackage::run][bin] $source_path is not valid file"
        exit 1
      elif [ -f $target_path ]; then
        local link_path=$(readlink $target_path)
        if [ "$link_path" != "$source_path" ]; then
          log::error "[$(timestamp)][zpackage::run][bin] name $bin has been used"
          exit 1
        fi
        return
      fi

      log::success "[$(timestamp)][zpackage::run] register zmicro bin: $(color::green ${bin})"
      ln -s $source_path $target_path
    }

    array::each run $(ls $package_bin_zmicro_path)
  fi
}

zpackage::unregister() {
  local name=$1
  if [ -z "$name" ]; then
    log::error "[$(timestamp)][zpackage::unregister] name is required"
    exit 1
  fi

  local package_path=$(zpackage::get_package_path $name)
  local package_bin_zmicro_path=$(zpackage::get_bin_zmicro_path $name)
  if [ -d $package_bin_zmicro_path ]; then
    run() {
      local index=$1
      local bin=$2
      local source_path=$package_path/bin/zmicro/$bin
      local target_path=$ZMICRO_HOME/bin/sub/$bin

      if [ ! -f $source_path ]; then
        log::error "[$(timestamp)][zpackage::run][bin] $source_path is not valid file"
        exit 1
      elif [ ! -f $target_path ]; then
        log::warn "[$(timestamp)][zpackage::run][bin] name $bin not found or removed"
        return
      fi

      log::success "[$(timestamp)][zpackage::run] unregister zmicro bin: $(color::red ${bin})"
      unlink $target_path
    }

    array::each run $(ls $package_bin_zmicro_path)
  fi
}

zpackage::clone() {
  local name=$1
  local version=$2
  local args=${@:3}

  if [ -z "$name" ]; then
    log::error "[$(timestamp)][zpackage::clone] name is required"
    exit 1
  fi

  if [ -z "$version" ]; then
    version=master
  fi

  local package_path=$(zpackage::get_package_path $name)
  local package_repo_url=$(zpackage::get_package_repo_url $name)
  if [ ! -d "$package_path" ]; then
    curl -I $package_repo_url 2>>/dev/null | head -n 1 | grep 404 >>/dev/null 2>&1
    if [ "$?" = "0" ]; then
      log::error "[$(timestamp)][zpackage::clone] package ${name} is not available"
      exit 1
    fi

    git clone $package_repo_url -b $version $package_path >>$ZMICRO_LOG_COMMON_PATH # 2>>$ZMICRO_LOG_ERROR_PATH
    if [ "$?" != "0" ]; then
      log::error "[$(timestamp)][zpackage::clone] error $name($version) ..."
      exit 1
    fi
  else
    cd $package_path
    git pull origin master >> $ZMICRO_LOG_UPDATE_PATH 2>>$ZMICRO_LOG_ERROR_PATH
    if [ "$?" != "0" ]; then
      echo "[$(timestamp)][zpackage::clone] update package repo ${name} ..."
      tail -n 50 $ZMICRO_LOG_ERROR_PATH
      exit -1
    fi
  fi
}

zpackage::install() {
  local name=$1
  local version=$2
  local args=${@:3}

  if [ -z "$name" ]; then
    log::error "[$(timestamp)][zpackage::install] name is required"
    exit 1
  fi

  if [ -z "$version" ]; then
    version=master
  fi

  zpackage::clone $name $version $args

  local package_path=$(zpackage::get_package_path $name)
  local action=install
  local package_action_path=$package_path/$action
  if [ ! -f "$package_action_path" ]; then
    log::error "[$(timestamp)][zpackage::install] package(${name}) found, but action(${action}) is not available"
    exit 1
  fi

  # 1. Before Run Action
  # 1.1 Load Config
  zpackage::load_config $name

  # 1.2 Load Core
  zpackage::load_core $name

  log::info "[$(timestamp)][zpackage::install] $name ..."
  $package_action_path $args

  # 3. After Run Action
  zpackage::register $name

  local version=$(zpackage::version $name)
  log::success "[$(timestamp)][zpackage::install][done] $name($version) ..."
}

zpackage::remove() {
  local name=$1
  local args=${@:2}

  if [ -z "$name" ]; then
    log::error "[$(timestamp)][zpackage::remove] name is required"
    exit 1
  fi

  local package_path=$(zpackage::get_package_path $name)
  if [ ! -d "$package_path" ]; then
    log::error "[$(timestamp)][zpackage::remove] package(${name}) not found"
    # exit 1
    return 
  fi

  local action=remove
  local package_action_path=$package_path/$action
  if [ ! -f "$package_action_path" ]; then
    log::error "[$(timestamp)][zpackage::remove] package(${name}) found, but action(${action}) is not available"
    exit 1
  fi

  log::info "[$(timestamp)][zpackage::remove] $name"


  # 1. Before Run Action
  # 1.1 Load Config
  zpackage::load_config $name

  # 1.2 Load Core
  zpackage::load_core $name

  # 2. Run Action
  # 2.1 Unregister bin
  zpackage::unregister $name
  # 2.2 Run Action
  $package_action_path $args

  rm -rf $package_path

  log::success "[$(timestamp)][zpackage::remove][done] $name"
}

zpackage::update() {
  local name=$1
  local version=$2
  local args=${@:3}

  if [ -z "$name" ]; then
    log::error "[$(timestamp)][zpackage::update] name is required"
    exit 1
  fi

  if [ -z "$version" ]; then
    version=master
  fi

  local package_path=$(zpackage::get_package_path $name)
  local package_repo_url=$(zpackage::get_package_repo_url $name)
  if [ ! -d "$package_path" ]; then
    log::error "[$(timestamp)][zpackage::update] package($name) not found"
    exit 1
  fi

  cd $package_path
  git pull origin master >> $ZMICRO_LOG_UPDATE_PATH 2>>$ZMICRO_LOG_ERROR_PATH
  if [ "$?" != "0" ]; then
    echo "[$(timestamp)][zpackage::update] update package repo ${name} ..."
    tail -n 50 $ZMICRO_LOG_ERROR_PATH
    exit -1
  fi

  local action=update
  local package_action_path=$package_path/$action
  if [ ! -f "$package_action_path" ]; then
    # log::error "[$(timestamp)][zpackage::update] package(${name}) found, but action(${action}) is not available"
    # exit 1
    zpackage::install $name $version $args
    return
  fi

  # 1. Before Run Action
  # 1.1 Load Config
  zpackage::load_config $name

  # 1.2 Load Core
  zpackage::load_core $name

  log::info "[$(timestamp)][zpackage::update] $name ..."
  $package_action_path $args

  # 3. After Run Action
  zpackage::register $name

  local version=$(zpackage::version $name)
  log::success "[$(timestamp)][zpackage::update][done] $name($version) ..."
}

zpackage::reinstall() {
  local name=$1
  if [ -z "$name" ]; then
    log::error "[$(timestamp)][zpackage::reinstall] name is required"
    exit 1
  fi

  log::info "[$(timestamp)][zpackage::reinstall] $name ..."

  local package_path=$(zpackage::get_package_path $name)
  zpackage::clone $name

  local action=reinstall
  local package_action_path=$package_path/$action
  if [ ! -f "$package_action_path" ]; then
    zpackage::install $name
  else
    $package_action_path $args
  fi

  local version=$(zpackage::version $name)
  log::success "[$(timestamp)][zpackage::reinstall][done] $name($version) ..."
}

zpackage::run() {
  local action=$1
  local name=$2
  local args=${@:3}

  if [ -z "$action" ]; then
    log::error "[$(timestamp)][zpackage::run] action is required"
    exit 1
  fi

  if [ -z "$name" ]; then
    log::error "[$(timestamp)][zpackage::run] name is required"
    exit 1
  fi

  # 2. Run Action => install / remove / update / reinstall
  log::debug "[$(timestamp)][zpackage::run] $name $action $args"
  case $action in
    install)
      zpackage::install $name
      ;;
    remove)
      zpackage::remove $name
      ;;
    reinstall)
      zpackage::reinstall $name
      ;;
    update)
      zpackage::update $name
      ;;
    *)
      log::error "[$(timestamp)][zpackage::run] unkown action(${action}) in ${action} ${name} ${args}"
      exit 1
      ;;
  esac
}

zpackage::list() {
  local packages=$(ls $PACKAGES_PATH)
  local index=1
  for name in $packages; do
    echo "[$index] $name"
    index=$((index + 1))
  done
}

zpackage::version() {
  local name=$1
  if [ -z "$name" ]; then
    log::error "[$(timestamp)][zpackage::version] name is required"
    exit 1
  fi

  # @TODO
  if [ ! -f "$(zpackage::get_mod_path $name)" ]; then
    echo "-"
    return
  fi

  zpackage::load_mod $name
  echo $VERSION
}

zpackage::description() {
  local name=$1
  if [ -z "$name" ]; then
    log::error "[$(timestamp)][zpackage::description] name is required"
    exit 1
  fi

  if [ ! -f "$(zpackage::get_mod_path $name)" ]; then
    echo "No Description"
    return
  fi

  zpackage::load_mod $name
  echo $DESCRIPTION
}

export -f zpackage::get_package_path
export -f zpackage::get_package_repo_url

export -f zpackage::get_mod_path
export -f zpackage::get_config_path
export -f zpackage::get_core_path
export -f zpackage::get_bin_zmicro_path

export -f zpackage::load_mod
export -f zpackage::load_config
export -f zpackage::load_core

export -f zpackage::register
export -f zpackage::unregister

export -f zpackage::clone

export -f zpackage::install
export -f zpackage::remove
export -f zpackage::update
export -f zpackage::reinstall

export -f zpackage::run
export -f zpackage::list

export -f zpackage::version
export -f zpackage::description

# export -f zpackage::release
